package me.caosh;

import me.caosh.jparser.model.Interface;
import me.caosh.jparser.model.Method;
import me.caosh.jparser.model.Parameter;
import me.caosh.jparser.parser.JavaLexer;
import me.caosh.jparser.parser.JavaParser;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;

/**
 * Hello world!
 */
public class TestHandlerGenerator {

    private static int indents = 0;
    private static PrintStream printStream;

    public static void main(String[] args) throws Exception {
        String sourcePath = args[0];
        InputStream inputStream = new FileInputStream(sourcePath);
        JavaParser parser = compileSource(inputStream);

        String namespace = args[1];

        Interface theInterface = parser.getInterface();
        String simpleName = theInterface.getName();
        String testHandlerName = getTestHandlerName(simpleName);

        String targetPath = getTargetPath(sourcePath, testHandlerName);
        System.out.println("Output: " + targetPath);
        FileOutputStream fileOutputStream = new FileOutputStream(targetPath);
        printStream = new PrintStream(fileOutputStream);

        println("package " + parser.getPackageDef() + ";\n");

        for (String imp : parser.getImports()) {
            println("import " + imp + ";");
        }
        println("import hbec.platform.commons.annotations.HbecFieldInit;");
        println("import hbec.platform.commons.annotations.HbecUriHandler;");
        println("import hbec.platform.commons.container.IJsonResponse;");
        println("import hbec.platform.commons.container.IRequest;");
        printStream.println();

        println("public class " + testHandlerName + " {");

        incIndent();
        String serviceMemberName = getServiceInstanceName(simpleName);
        println("@HbecFieldInit");
        println("private " + simpleName + " " + serviceMemberName + ";\n");

        List<Method> methods = parser.getMethods();
        for (Method method : methods) {
            generateMethodTest(namespace, serviceMemberName, method);
        }

        decIndent();
        println("}");
        printStream.close();
    }

    private static void incIndent() {
        indents++;
    }

    private static void decIndent() {
        indents--;
    }

    private static void println(String line) {
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < indents; i++) {
            content.append("    "); // 4 spaces
        }
        content.append(line);
        printStream.println(content);
    }

    private static String getTargetPath(String sourcePath, String testHandlerName) {
        int last = sourcePath.lastIndexOf("\\");
        String directory = sourcePath.substring(0, last);
        return directory + "\\" + testHandlerName + ".java";
    }

    private static void generateMethodTest(String namespace, String serviceMemberName, Method method) throws IOException {
        String methodName = method.getName();
        String testMethodName = getTestMethodName(methodName);
        println("@HbecUriHandler(uris = \"/" + namespace + "/" + methodName + "\")");
        println("public void " + testMethodName + "(IRequest request, IJsonResponse response) throws Exception {");
        incIndent();
        println("try {");

        incIndent();
        for (Parameter parameter : method.getParameterList()) {
            println(parameter.getType() + " " + parameter.getName() + " = " + getParameterConverter(parameter) + ";");
        }
        if ("void".equals(method.getReturns())) {
            println("response.setData(\"ok\");");
        } else {
            println(method.getReturns() + " ret = " + serviceMemberName + "." + methodName + "(" + buildMethodCallerParameters(method) + ");");
            println("response.setData(ret);");
        }
        decIndent();
        println("} catch (Exception e) {");
        incIndent();
        println("e.printStackTrace();");
        println("response.setData(e.getMessage());");
        decIndent();
        println("}");
        decIndent();
        println("}\n");
    }

    private static String buildMethodCallerParameters(Method method) {
        StringBuilder parameterChain = new StringBuilder();

        for (Parameter parameter : method.getParameterList()) {
            parameterChain.append(parameter.getName()).append(", ");
        }

        int lastComma = parameterChain.lastIndexOf(",");
        if (lastComma > 0) {
            parameterChain.delete(lastComma, parameterChain.length());
        }
        return parameterChain.toString();
    }

    private static String getParameterConverter(Parameter parameter) {
        switch (parameter.getType()) {
            case "String":
                return buildStringValueGetter(parameter);
            case "Integer":
            case "int":
                return buildIntegerValueGetter(parameter);
            default:
                return buildBeanValueGetter(parameter);
        }
    }

    private static String buildBeanValueGetter(Parameter parameter) {
        return "request.getBean(new " + parameter.getType() + "())";
    }

    private static String buildIntegerValueGetter(Parameter parameter) {
        return "Integer.valueOf(" + buildStringValueGetter(parameter) + ")";
    }

    private static String buildStringValueGetter(Parameter parameter) {
        return "request.getParams().getValue(\"" + parameter.getName() + "\")";
    }

    private static String getTestMethodName(String methodName) {
        return "test" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1);
    }


    private static String getTestHandlerName(String simpleName) {
        if (Character.isUpperCase(simpleName.charAt(0)) && Character.isUpperCase(simpleName.charAt(1))) {
            return simpleName.substring(1) + "TestHandler";
        } else {
            return simpleName + "TestHandler";
        }
    }

    private static String getServiceInstanceName(String simpleName) {
        if (Character.isUpperCase(simpleName.charAt(0)) && Character.isUpperCase(simpleName.charAt(1))) {
            return Character.toLowerCase(simpleName.charAt(1)) + simpleName.substring(2);
        } else {
            return Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
        }
    }

    private static JavaParser compileSource(InputStream inputStream) throws IOException {
        JavaLexer lexer = new JavaLexer(new ANTLRInputStream(inputStream));
        CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
        JavaParser parser = new JavaParser(commonTokenStream);
        parser.java();
        return parser;
    }
}
